import {useCallback} from 'react';

import {fetchTagValues} from 'sentry/actionCreators/tags';
import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder';
import type {FilterKeySection} from 'sentry/components/searchQueryBuilder/types';
import {defaultConfig, InvalidReason} from 'sentry/components/searchSyntax/parser';
import {t} from 'sentry/locale';
import type {PageFilters} from 'sentry/types/core';
import type {Tag, TagCollection} from 'sentry/types/group';
import {SavedSearchType} from 'sentry/types/group';
import useApi from 'sentry/utils/useApi';
import useOrganization from 'sentry/utils/useOrganization';
import type {
  SearchBarData,
  SearchBarDataProviderProps,
  WidgetBuilderSearchBarProps,
} from 'sentry/views/dashboards/datasetConfig/base';
import type {WidgetQuery} from 'sentry/views/dashboards/types';
import {
  SESSION_STATUSES,
  SESSIONS_FILTER_TAGS,
} from 'sentry/views/dashboards/widgetBuilder/releaseWidget/fields';

const invalidMessages = {
  ...defaultConfig.invalidMessages,
  [InvalidReason.WILDCARD_NOT_ALLOWED]: t("Release queries don't support wildcards."),
  [InvalidReason.FREE_TEXT_NOT_ALLOWED]: t(
    "Release queries don't support free text search."
  ),
};

interface Props {
  onClose: WidgetBuilderSearchBarProps['onClose'];
  pageFilters: PageFilters;
  widgetQuery: WidgetQuery;
  portalTarget?: HTMLElement | null;
}

export function ReleaseSearchBar({
  onClose,
  pageFilters,
  widgetQuery,
  portalTarget,
}: Props) {
  const {getFilterKeys, getFilterKeySections, getTagValues} =
    useReleasesSearchBarDataProvider({pageFilters});

  return (
    <SearchQueryBuilder
      initialQuery={widgetQuery.conditions}
      filterKeySections={getFilterKeySections()}
      filterKeys={getFilterKeys()}
      getTagValues={getTagValues}
      placeholder={t('Search for release version, session status, and more')}
      onChange={(query, state) => {
        onClose?.(query, {validSearch: state.queryIsValid});
      }}
      searchSource="widget_builder"
      disallowWildcard
      disallowUnsupportedFilters
      disallowFreeText
      invalidMessages={invalidMessages}
      recentSearches={SavedSearchType.SESSION}
      portalTarget={portalTarget}
    />
  );
}

export function useReleasesSearchBarDataProvider(
  props: SearchBarDataProviderProps
): SearchBarData {
  const {pageFilters} = props;
  const organization = useOrganization();
  const api = useApi();

  const getFilterKeySections = useCallback((): FilterKeySection[] => {
    return [
      {value: 'session_field', label: t('Suggested'), children: SESSIONS_FILTER_TAGS},
    ];
  }, []);

  const getFilterKeys = useCallback((): TagCollection => {
    return Object.values(SESSIONS_FILTER_TAGS).reduce((acc, key) => {
      // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
      acc[key] = {key, name: key};
      return acc;
    }, {});
  }, []);

  const getTagValues = useCallback(
    (tag: Tag, searchQuery: string): Promise<string[]> => {
      if (tag.name === 'session.status') {
        return Promise.resolve(SESSION_STATUSES);
      }
      const projectIdStrings = pageFilters.projects?.map(String);
      return fetchTagValues({
        api,
        orgSlug: organization.slug,
        tagKey: tag.key,
        search: searchQuery,
        projectIds: projectIdStrings,
        includeTransactions: true,
      }).then(
        tagValues => tagValues.map(({value}) => value),
        () => {
          throw new Error('Unable to fetch tag values');
        }
      );
    },
    [api, organization.slug, pageFilters.projects]
  );

  return {
    getFilterKeys,
    getFilterKeySections,
    getTagValues,
  };
}
